package linux;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.net.whois.WhoisClient;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import jodd.io.NetUtil;

public class LinuxMaintainers {
	
	private static ObjectMapper objMapper = new ObjectMapper();
	private static List<Map<String, String>> tlds= new ArrayList<>();
	private static Map<String, String> domainCountryMap = new HashMap<>();
	
	static {
		try {// load all top level domains
			tlds.addAll( objMapper.readValue(ClassLoader.getSystemResourceAsStream("top_level_domains.json"), new TypeReference<List<Map<String, String>>>() {}));
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {// load all known domains
			domainCountryMap.putAll( objMapper.readValue(ClassLoader.getSystemResourceAsStream("domains_with_country.json"), new TypeReference<Map<String, String>>() {}));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		
		// fetch linux maintainers
		String maintainerFileUrl = "https://github.com/torvalds/linux/raw/refs/heads/master/MAINTAINERS";
		List<String> maintainerFileLines = new ArrayList<>();
		try {
			maintainerFileLines.addAll(
					Arrays.asList(NetUtil.downloadString(maintainerFileUrl, StandardCharsets.UTF_8).split("\n")));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// group maintainers email by domain
		Map<String, List<String>> domainEmailsMap  = maintainerFileLines.stream()
				.filter(l -> l.startsWith("M:")) // get only  maintainers
				.map(l -> l.substring(2).trim())
				.distinct()
				.map(l -> { // get email
					// just "torvalds@linux-foundation.org"
					String email = l;
					if (l.contains("<") && l.contains(">")) {
						// format like "Linus Torvalds <torvalds@linux-foundation.org>"
						email = l.substring(l.indexOf("<") + 1, l.indexOf(">"));
					}
					return email;
				})
				.distinct()
				.collect(Collectors.groupingBy(l -> l.substring(l.indexOf("@") + 1)));
		
		// sort domain by email count
		List<String> domains = new ArrayList<>(domainEmailsMap.keySet());
		domains.sort((a, b) -> domainEmailsMap.get(b).size() - domainEmailsMap.get(a).size());
		
		// print domain email count
		System.out.println("***Domains***: " + domainEmailsMap.size());
		AtomicInteger domainMaxLen = new AtomicInteger(30);
		domains.stream().max((a,b)->a.length()-b.length()).ifPresent(ml->domainMaxLen.set(ml.length()));
		for (String domain : domains) {
			List<String> emails = domainEmailsMap.get(domain);
			System.out.printf("===%s===(%s):%s%n"
					, StringUtils.rightPad(domain, domainMaxLen.get())
					, StringUtils.rightPad(""+emails.size(),4)
					,formatCount(emails.size()));
		}
		
			
		// group maintainers email by country
		Map<String, List<String>> countryEmailsMap = new HashMap<>();
		domains.forEach(domain->{
			String country = getCountryFromDomain(domain);
			if(country==null){
				country = "UNKNOWN";
				System.out.println("cannot find country for "+domain);
			}
			List<String> emailLst = countryEmailsMap.getOrDefault(country, new ArrayList<>());
			emailLst.addAll( domainEmailsMap.get(domain));
			countryEmailsMap.put(country, emailLst);
		});
		// sort country by email count
		List<String> countries = new ArrayList<>(countryEmailsMap.keySet());
		countries.sort((a,b)->countryEmailsMap.get(b).size()-countryEmailsMap.get(a).size());
		
		// print country email count
		System.out.println("***Countries***: " + countryEmailsMap.size());
		for (String country : countries) {
			List<String> emails = countryEmailsMap.get(country);
			int emailCount = emails.size();
			System.out.printf("===%s(%d)\t===:%s%n",country, emailCount,formatCount(emailCount));
		}
		System.out.println("***Countries***: " + countryEmailsMap.size());
		System.out.println("(without gmail)");
		for (String country : countries) {
			List<String> emails = countryEmailsMap.get(country).stream().filter(e->!e.contains("@gmail.com")).collect(Collectors.toList());
			int emailCount = emails.size();
			System.out.printf("===%s(%d)\t===:%s%n",country, emailCount,formatCount(emailCount));
		}
	}
	
	private static String formatCount(int count) {
		int blocks = count /4;
		int remains = count % 4;
		String left = "";
		switch(remains) {
			case 1:
				left = "\u258E";
				break;
			case 2:
				left = "\u258C";
				break;
			case 3:
				left = "\u258A";
				break;
			default:
				break;
			
		}
		return StringUtils.repeat("\u2588", blocks) +left;
	}

	private static String getCountryFromDomain(String domain) {
		
	
		
		String targetDomain = domain;
		String[] split = domain.split("\\.");
		if(split.length>2){
			targetDomain = split[split.length-2]+"."+split[split.length-1];
		}
		if(domainCountryMap.get(targetDomain)!=null) {
			return domainCountryMap.get(targetDomain).toUpperCase();
		}
		String country = null;
		// query whois server by http
		try {
			String whoisUrl = "https://www.whois.com/whois/"+targetDomain;
			
			String body =  NetUtil.downloadString(whoisUrl, StandardCharsets.UTF_8);
			int registrarDataStart = body.toUpperCase().lastIndexOf("REGISTRANT COUNTRY");
			registrarDataStart = body.toUpperCase().indexOf(":", registrarDataStart) + 1;
			int registrarDataEnd = body.toUpperCase().indexOf("\n", registrarDataStart);
			country =body.substring(registrarDataStart, registrarDataEnd).trim();
			return country.toUpperCase();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}
